package cn.com.twoke.app.pin_dou_dou.color;

import cn.com.twoke.app.pin_dou_dou.utils.ColorLabUtils;

import java.awt.*;
/**
 * 计算 ΔE*00 色差（CIEDE2000标准）
 * 目前最精确的色差公式
 */
public class CIE_2000 implements ColorDifferenceCalculator{

    @Override
    public double deltaE(Color color1, Color color2) {
        double[] lab1 = ColorLabUtils.rgbToLab(color1);
        double[] lab2 = ColorLabUtils.rgbToLab(color2);

        return calculateCIEDE2000(
                lab1[0], lab1[1], lab1[2],
                lab2[0], lab2[1], lab2[2]
        );
    }

    /**
     * CIEDE2000 色差计算实现
     */
    public static double calculateCIEDE2000(double l1, double a1, double b1,
                                             double l2, double a2, double b2) {
        // 实现 CIEDE2000 算法
        double avgL = (l1 + l2) / 2.0;

        double c1 = Math.sqrt(a1 * a1 + b1 * b1);
        double c2 = Math.sqrt(a2 * a2 + b2 * b2);
        double avgC = (c1 + c2) / 2.0;

        double g = 0.5 * (1 - Math.sqrt(Math.pow(avgC, 7) / (Math.pow(avgC, 7) + Math.pow(25, 7))));

        double a1Prime = a1 * (1 + g);
        double a2Prime = a2 * (1 + g);

        double c1Prime = Math.sqrt(a1Prime * a1Prime + b1 * b1);
        double c2Prime = Math.sqrt(a2Prime * a2Prime + b2 * b2);
        double avgCPrime = (c1Prime + c2Prime) / 2.0;

        double h1Prime = Math.toDegrees(Math.atan2(b1, a1Prime));
        if (h1Prime < 0) h1Prime += 360;
        double h2Prime = Math.toDegrees(Math.atan2(b2, a2Prime));
        if (h2Prime < 0) h2Prime += 360;

        double deltaLPrime = l2 - l1;
        double deltaCPrime = c2Prime - c1Prime;

        double deltahPrime;
        if (Math.abs(h1Prime - h2Prime) <= 180) {
            deltahPrime = h2Prime - h1Prime;
        } else if (h2Prime <= h1Prime) {
            deltahPrime = h2Prime - h1Prime + 360;
        } else {
            deltahPrime = h2Prime - h1Prime - 360;
        }

        double deltaHPrime = 2 * Math.sqrt(c1Prime * c2Prime) * Math.sin(Math.toRadians(deltahPrime / 2));

        double avgHPrime;
        if (Math.abs(h1Prime - h2Prime) > 180) {
            avgHPrime = (h1Prime + h2Prime + 360) / 2.0;
        } else {
            avgHPrime = (h1Prime + h2Prime) / 2.0;
        }

        double t = 1 - 0.17 * Math.cos(Math.toRadians(avgHPrime - 30)) +
                0.24 * Math.cos(Math.toRadians(2 * avgHPrime)) +
                0.32 * Math.cos(Math.toRadians(3 * avgHPrime + 6)) -
                0.20 * Math.cos(Math.toRadians(4 * avgHPrime - 63));

        double sl = 1 + (0.015 * Math.pow(avgL - 50, 2)) / Math.sqrt(20 + Math.pow(avgL - 50, 2));
        double sc = 1 + 0.045 * avgCPrime;
        double sh = 1 + 0.015 * avgCPrime * t;

        double rt = -2 * Math.sqrt(Math.pow(avgCPrime, 7) / (Math.pow(avgCPrime, 7) + Math.pow(25, 7))) *
                Math.sin(Math.toRadians(60 * Math.exp(-Math.pow((avgHPrime - 275) / 25, 2))));

        double deltaE = Math.sqrt(
                Math.pow(deltaLPrime / sl, 2) +
                        Math.pow(deltaCPrime / sc, 2) +
                        Math.pow(deltaHPrime / sh, 2) +
                        rt * (deltaCPrime / sc) * (deltaHPrime / sh)
        );

        return deltaE;
    }

}
